{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# パーセプトロン(perceptron)\n",
    "\n",
    "* パーセプトロンとは\n",
    "    * 複数の信号を入力として受け取り、一つの「信号」を出力するためのアルゴリズム\n",
    "        * ここでのパーセプトロンは正確には「人工ニューロン」や「単純パーセプトロン」と呼ばれる\n",
    "    * ニューラルネットワークの起源となるアルゴリズム\n",
    "    * ローゼンブラットというアメリカの研究者によって19571年に考案された\n",
    "    * 信号\n",
    "        * 電流や川のような流れをもつもののイメージ\n",
    "        * 二値の値 (信号を「**流す(1)/流さない(0)**」)\n",
    "\n",
    "## パーセプトロンの動作原理\n",
    "\n",
    "<img src=\"img/2-1.png\" alt=\"2入力のパーセプトロン\" title=\"2入力のパーセプトロン\" width=\"300\" height=\"300\" align=\"left\" />  \n",
    "\n",
    "<br clear=\"left\">\n",
    "\n",
    "$\n",
    "y = \n",
    "\\begin{eqnarray}\n",
    "\\left\\{\n",
    "\\begin{array}{l}\n",
    "0 & ( \\omega_{1} x_{1} + \\omega_{2} x_{2} \\leqq \\theta ) \\\\\n",
    "1 & ( \\omega_{1} x_{1} + \\omega_{2} x_{2} > \\theta ) \n",
    "\\end{array}\n",
    "\\right.\n",
    "\\end{eqnarray}\n",
    "$\n",
    "\n",
    "* $ x_{1}, x_{2} $ は入力信号\n",
    "* $ \\omega_{1}, \\omega_{2} $ は重み\n",
    "* $ \\theta $ は閾値\n",
    "\n",
    " \n",
    " * 図中の「○」はニューロン(もしくはノード)\n",
    "     1. 入力信号は、ニューロンに送られる際にそれぞれに固有の重みが乗算される\n",
    "         1. 重みが大きいほどその信号の重要性が高くなる\n",
    "         1. 重みは電流で言うところの抵抗に相当 (ただし、パーセプトロンは**重みが大きいほど大きな信号が流れる**ので電流の抵抗とは逆)\n",
    "     1. ニューロンでは送られてきた信号の総和が計算される\n",
    "     1. 総和がある閾値(限界値)を超えた場合に1を出力する (ニューロンが発火すると表現することもある)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 単純な論理回路\n",
    "\n",
    "* ANDゲート\n",
    "    * 2入力1出力のゲート\n",
    "        * 2つの入力が1のときのみ1を出力\n",
    "        * それ以外は0を出力する\n",
    "\n",
    "| $ x_1 $ | $ x_2$ | y |\n",
    "|:---:|:---:|:---:|\n",
    "| 0 | 0 | 0 |\n",
    "| 1 | 0 | 0 |\n",
    "| 0 | 1 | 0 |\n",
    "| 1 | 1 | 1 |\n",
    "入力信号と出力信号の対応表(真理値表)\n",
    "\n",
    "* ここで行うことは真理値表を満たすように $ \\omega_{1} x_{1}, \\omega_{1} x_{2}, \\theta $ を決める\n",
    "    * $ (\\omega_{1}, \\omega_{2}, \\theta ) = (0.5, 0.5, 0.9) $\n",
    "    * $ (\\omega_{1}, \\omega_{2}, \\theta ) = (1, 1, 1) $\n",
    "    * のように無限にパラメータを選ぶことができる\n",
    "\n",
    "---\n",
    "\n",
    "* NAND(not AND)ゲート\n",
    "\n",
    "| $ x_1 $ | $ x_2$ | y |\n",
    "|:---:|:---:|:---:|\n",
    "| 0 | 0 | 1 |\n",
    "| 1 | 0 | 1 |\n",
    "| 0 | 1 | 1 |\n",
    "| 1 | 1 | 0 |\n",
    "\n",
    "* $ (\\omega_{1}, \\omega_{2}, \\theta ) = (-0.5, -0.5, -0.9) $\n",
    "* のように無限にパラメータを選ぶことができる\n",
    "    * ANDゲートのパラメータの符号を反転するとNANDゲートを実現できる\n",
    "\n",
    "---\n",
    "\n",
    "* ORゲート\n",
    "\n",
    "| $ x_1 $ | $ x_2$ | y |\n",
    "|:---:|:---:|:---:|\n",
    "| 0 | 0 | 0 |\n",
    "| 1 | 0 | 1 |\n",
    "| 0 | 1 | 1 |\n",
    "| 1 | 1 | 1 |\n",
    "\n",
    "* 入力信号が少なくとも一つが1であれば出力が1になる論理回路\n",
    "* $ (\\omega_{1}, \\omega_{2}, \\theta ) = (0.9, 0.9, 0.5) $\n",
    "* のように無限にパラメータを選ぶことができる\n",
    "\n",
    "---\n",
    "\n",
    "* 今はパーセプトロンのパラメータを決めているのは人間\n",
    "    * 真理値表という「学習データ」を見ながら人の手によってパラメータ値を考えている\n",
    "    * 機械学習では、この**パラメータの値を決める作業をコンピュータに自動で行わせる。**\n",
    "    * 学習とは\n",
    "        * 適切なパラメータを決める作業\n",
    "        * 人が行う仕事はパーセプトロンの構造(モデル)を考えてコンピュータに学習データを与えること\n",
    "* **パーセプトロンの構造は、AND,NAND,ORゲートの全てで同じ**\n",
    "    * 3つのゲートで異なるのはパラメータ(重みと閾値)の値だけ\n",
    "    * 同じ構造のパーセプトロンでパラメータや閾値を適切に調整するだけでゲートが変わっている"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## パーセプトロンの実装"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---AND---\n",
      "0\n",
      "0\n",
      "0\n",
      "1\n",
      "---NAND---\n",
      "1\n",
      "1\n",
      "1\n",
      "0\n",
      "---OR---\n",
      "0\n",
      "1\n",
      "1\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "def AND(x1, x2):\n",
    "    w1, w2, theta = 0.5, 0.5, 0.9\n",
    "    tmp = x1*w1 + x2*w2\n",
    "    if tmp <= theta:\n",
    "        return 0\n",
    "    elif tmp > theta:\n",
    "        return 1\n",
    "\n",
    "print ('---AND---')\n",
    "print (AND(0,0))\n",
    "print (AND(1,0))\n",
    "print (AND(0,1))\n",
    "print (AND(1,1))\n",
    "\n",
    "def NAND(x1, x2):\n",
    "    w1, w2, theta = -0.5, -0.5, -0.9\n",
    "    tmp = x1*w1 + x2*w2\n",
    "    if tmp <= theta:\n",
    "        return 0\n",
    "    elif tmp > theta:\n",
    "        return 1\n",
    "\n",
    "print ('---NAND---')\n",
    "print (NAND(0,0))\n",
    "print (NAND(1,0))\n",
    "print (NAND(0,1))\n",
    "print (NAND(1,1))\n",
    "\n",
    "def OR(x1, x2):\n",
    "    w1, w2, theta = 0.9, 0.9, 0.5\n",
    "    tmp = x1*w1 + x2*w2\n",
    "    if tmp <= theta:\n",
    "        return 0\n",
    "    elif tmp > theta:\n",
    "        return 1\n",
    "\n",
    "print ('---OR---')\n",
    "print (OR(0,0))\n",
    "print (OR(1,0))\n",
    "print (OR(0,1))\n",
    "print (OR(1,1))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 重みとバイアスの導入\n",
    "\n",
    "パーセプトロンの別の実装\n",
    "\n",
    "$\n",
    "y = \n",
    "\\begin{eqnarray}\n",
    "\\left\\{\n",
    "\\begin{array}{l}\n",
    "0 & ( b + \\omega_{1} x_{1} + \\omega_{2} x_{2} \\leqq 0 ) \\\\\n",
    "1 & ( b + \\omega_{1} x_{1} + \\omega_{2} x_{2} > 0 ) \n",
    "\\end{array}\n",
    "\\right.\n",
    "\\end{eqnarray}\n",
    "$\n",
    "\n",
    "* $ x_{1}, x_{2} $ は入力信号\n",
    "* $ \\omega_{1}, \\omega_{2} $ は重み\n",
    "* $ b $ はバイアス\n",
    "    * 文脈によっては $ b $ も重みと呼ばれることもある\n",
    "\n",
    "---\n",
    "\n",
    "* 最初の実装の $ \\theta $ を $ b $ とし、左辺に移動しただけ\n",
    "    * バイアスは重みとは別の働きをする\n",
    "        * 重みは入力信号の重要度をコントロールする\n",
    "        * バイアスは**発火のしやすさ(出力信号が1を出力する度合い)**を調整する\n",
    "        * バイアスには「ゲタはき」という意味があり、入力が何もないとき出力にどれだけゲタをはかせるか(上乗せするか)ということを意味する\n",
    "* 入力信号に重みが乗算された値とバイアスの我が0を上回れば1を出力、そうでなければ0を出力"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "w*x = [ 0.   0.5]\n",
      "np.sum(w*x) = 0.5\n",
      "np.sum(w*x) + b = -0.19999999999999996\n",
      "---AND---\n",
      "0\n",
      "0\n",
      "0\n",
      "1\n",
      "---NAND---\n",
      "1\n",
      "1\n",
      "1\n",
      "0\n",
      "---OR---\n",
      "0\n",
      "1\n",
      "1\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "x = np.array([0,1])\n",
    "w = np.array([0.5, 0.5])\n",
    "b = -0.7\n",
    "print (\"w*x = {0}\".format(w*x))\n",
    "print (\"np.sum(w*x) = {0}\".format(np.sum(w*x)))\n",
    "print (\"np.sum(w*x) + b = {0}\".format(np.sum(w*x) + b))\n",
    "\n",
    "### AND\n",
    "def AND(x1,x2):\n",
    "    x = np.array([x1, x2])\n",
    "    w = np.array([0.5, 0.5])\n",
    "    b = -0.7\n",
    "    tmp = np.sum(w*x) + b\n",
    "    if tmp <= 0:\n",
    "        return 0\n",
    "    else:\n",
    "        return 1\n",
    "\n",
    "print ('---AND---')\n",
    "print (AND(0,0))\n",
    "print (AND(1,0))\n",
    "print (AND(0,1))\n",
    "print (AND(1,1))\n",
    "\n",
    "### NAND\n",
    "def NAND(x1,x2):\n",
    "    x = np.array([x1, x2])\n",
    "    w = np.array([-0.5, -0.5])\n",
    "    b = 0.7\n",
    "    tmp = np.sum(w*x) + b\n",
    "    if tmp <= 0:\n",
    "        return 0\n",
    "    else:\n",
    "        return 1\n",
    "\n",
    "print ('---NAND---')\n",
    "print (NAND(0,0))\n",
    "print (NAND(1,0))\n",
    "print (NAND(0,1))\n",
    "print (NAND(1,1))\n",
    "\n",
    "\n",
    "### OR\n",
    "def OR(x1,x2):\n",
    "    x = np.array([x1, x2])\n",
    "    w = np.array([0.9, 0.9])\n",
    "    b = -0.5\n",
    "    tmp = np.sum(w*x) + b\n",
    "    if tmp <= 0:\n",
    "        return 0\n",
    "    else:\n",
    "        return 1\n",
    "\n",
    "print ('---OR---')\n",
    "print (OR(0,0))\n",
    "print (OR(1,0))\n",
    "print (OR(0,1))\n",
    "print (OR(1,1))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## パーセプトロンの限界\n",
    "\n",
    "### XORゲート\n",
    "\n",
    "* XORゲート(排他的論理和)\n",
    "    * $x_1, x_2$ どちらか片方が1のときだけ出力が1になる\n",
    "\n",
    "| $ x_1 $ | $ x_2$ | y |\n",
    "|:---:|:---:|:---:|\n",
    "| 0 | 0 | 0 |\n",
    "| 1 | 0 | 1 |\n",
    "| 0 | 1 | 1 |\n",
    "| 1 | 1 | 0 |\n",
    "\n",
    "* 一本の直線で0と1の状態を分けることができないため、これまでの式ではXORゲートの実装は不可能\n",
    "    * パーセプトロンは**1本の直線で分けた領域(線形な領域)だけしか表現できない**\n",
    "        * 曲線による領域(非線形な領域)では表現できる\n",
    "        * もしくは、「層を重ねる」事で表現できる\n",
    "            * パーセプトロンの限界を正確に言うと、「単層のパーセプトロンではXORゲートを表現できない」\n",
    "            * もしくは、「単層のパーセプトロンでは非線形領域は分離できない」\n",
    "\n",
    "### 多層パーセプトロン\n",
    "\n",
    "<img src=\"img/2-11.png\" alt=\"組み合わせでXORゲートを実現\" title=\"組み合わせでXORゲートを実現\" />\n",
    "\n",
    "* NANDの出力は $ s_1 $\n",
    "* ORの出力は $ s_2 $\n",
    "* 一番右はAND\n",
    "\n",
    "| $ x_1 $ | $ x_2$ | $ s_1 $ | $ s_2$ | y |\n",
    "|:---:|:---:|:---:|:---:|:---:|\n",
    "| 0 | 0 | 1 | 0 | 0 |\n",
    "| 1 | 0 | 1 | 1 | 1 |\n",
    "| 0 | 1 | 1 | 1 | 1 |\n",
    "| 1 | 1 | 0 | 1 | 0 |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---XOR---\n",
      "0\n",
      "1\n",
      "1\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "### AND\n",
    "def AND(x1,x2):\n",
    "    x = np.array([x1, x2])\n",
    "    w = np.array([0.5, 0.5])\n",
    "    b = -0.7\n",
    "    tmp = np.sum(w*x) + b\n",
    "    if tmp <= 0:\n",
    "        return 0\n",
    "    else:\n",
    "        return 1\n",
    "\n",
    "### NAND\n",
    "def NAND(x1,x2):\n",
    "    x = np.array([x1, x2])\n",
    "    w = np.array([-0.5, -0.5])\n",
    "    b = 0.7\n",
    "    tmp = np.sum(w*x) + b\n",
    "    if tmp <= 0:\n",
    "        return 0\n",
    "    else:\n",
    "        return 1\n",
    "\n",
    "### OR\n",
    "def OR(x1,x2):\n",
    "    x = np.array([x1, x2])\n",
    "    w = np.array([0.9, 0.9])\n",
    "    b = -0.5\n",
    "    tmp = np.sum(w*x) + b\n",
    "    if tmp <= 0:\n",
    "        return 0\n",
    "    else:\n",
    "        return 1\n",
    "\n",
    "### XOR\n",
    "def XOR(x1, x2):\n",
    "    s1 = NAND(x1, x2)\n",
    "    s2 = OR(x1, x2)\n",
    "    y = AND(s1, s2)\n",
    "    return y\n",
    "\n",
    "print ('---XOR---')\n",
    "print (XOR(0,0))\n",
    "print (XOR(1,0))\n",
    "print (XOR(0,1))\n",
    "print (XOR(1,1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "<img src=\"img/2-13.png\" alt=\"XORのパーセプトロンによる表記\" title=\"XORのパーセプトロンによる表記\" />\n",
    "\n",
    "* XORは上図のような多層構造のネットワーク\n",
    "    * 一番左の段 : 第0層\n",
    "    * その右の段 : 第1層\n",
    "    * 一番右の段 : 第2層\n",
    "* XORは**多層パーセプトロン (multi-layered perceptron)**\n",
    "    * ANDやORは単層パーセプトロン\n",
    "* ここでXORは「2層のパーセプトロン」と呼ぶことにする\n",
    "    * 実質重みの持つ層は2つのため\n",
    "        文献によっては層の数をそのまま数えて「2層のパーセプトロン」と呼ぶ場合もある\n",
    "* 2層のパーセプトロンの動作\n",
    "    1. 第0層の2つのニューロンが入力信号を受け取り、第1層のニューロンへ信号を送る\n",
    "    2. 第1層のニューロンが第2層のニューロンへ信号を送り、第2層目のニューロンは $ y $ を出力する\n",
    "\n",
    "---\n",
    "\n",
    "* パーセプトロンは層を重ねることでより柔軟な表現が可能になる\n",
    "   * NANDゲートの組み合わせだけでコンピュータの行う処理を再現することができる\n",
    "       * パーセプトロンでもコンピュータを表現できる\n",
    "           * パーセプトロンの組み合わせは、幾層にも重なった単一のパーセプトロンとして表現できる\n",
    "   * 理論上は2層のパーセプトロンであればコンピュータを作れる\n",
    "       * 活性化関数に非線形なシグモイド関数を用いた２層のパーセプトロン\n",
    "       * ただ、実際に2層のパーセプトロンでコンピュータを作るのはかなり大変\n",
    "           * コンピュータの表現も、層が幾重にも重なった構造として作られるのが自然\n",
    "           * AND やORゲート -> 半加算器、全加算器 -> 算術論理演算装置(ALU) -> CPU\n",
    "               * 必要なモジュールを段階的に作り上げていくのが自然"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
